import numpy as np
import math
import matplotlib.pyplot as plt

def compute_model_output(x,w,b):
    m = x.shape[0]
    f_wb = np.zeros(m)
    for ii_ in range(0,m,1):
        f_wb[ii_] = w*x[ii_]+b
    return f_wb

def compute_cost(x,y,w,b):
    m = x.shape[0]
    cost_sum = 0
    for ii_ in range(0,m,1):
        f_wb = w*x[ii_]+b
        cost = (f_wb-y[ii_])**2
        cost_sum = cost_sum + cost
    total_cost = 1/(2*m)*cost_sum
    return total_cost

def compute_gradient(x,y,w,b):
    m = x.shape[0]
    dj_dw = 0
    dj_db = 0
    for ii_ in range(0,m,1):
        f_wb = w*x[ii_]+b
        dj_dw = dj_dw + (f_wb-y[ii_])*x[ii_]
        dj_db = dj_db + (f_wb-y[ii_])
    dj_dw = dj_dw/m
    dj_db = dj_db/m
    return dj_dw,dj_db

def gradient_descent(x,y,w,b,alpha,num_inters,cost_function,gradient_function):
    J_history = []
    p_history = []

    for ii_ in range(0,num_inters,1):
        dj_dw,dj_db = gradient_function(x,y,w,b)
        w = w - alpha*dj_dw
        b = b - alpha*dj_db

        J_history.append(cost_function(x,y,w,b))
        p_history.append([w,b])

        if ii_%math.ceil(num_inters/10)==0:
            print(f'Interation:{ii_:4}'   #:是让前面连值共占4个位置（调整格式用的）
                  f'Cost:{J_history[-1]:0.2e}'#:0.2e也是格式，保留2位小数;a[-1]是取最后一位
                  f'dj_dw:{dj_dw:0.3e},dj_db:{dj_db:0.3e}'
                  f'w:{w:0.3e},b:{b:0.3e}')
    return w,b,J_history,p_history

x_train = np.array([1,2,3,4,5,6,7,8,9,10])
y_train = np.array([0,98,52,220,324,436,550,650,700,800])

w_init = 0
b_init = 0
w_final,b_final,J_history,p_history = gradient_descent(x_train,y_train,w_init,b_init,1.0e-2,1000,compute_cost,compute_gradient)
print(f'(w,b)found by gradient descent is ({w_final},{b_final})')

fig,(ax1,ax2,ax3) = plt.subplots(1,3,constrained_layout=True,figsize=(12,4))
ax1.plot(J_history)
ax2.scatter(x_train,y_train,c='r',label='Actual Values')
ax2.plot(x_train,compute_model_output(x_train,w_final,b_final),c='b',label='Prediction Model')
ax2.set_title('No meanings')
ax2.set_xlabel('x')
ax2.set_ylabel('y')
ax2.legend()
w0,b0 = np.meshgrid(np.arange(-100,200,10),np.arange(-200,100,10))#一个横着递增，一个竖着递增。的两个矩阵
z = np.zeros_like(w0)
for ii_ in range(0,w0.shape[0],1):
    for jj_ in range(0,w0.shape[1],1):
        z[ii_][jj_]=compute_cost(x_train,y_train,w0[ii_][jj_],b0[ii_][jj_])
ax3.contour(w0,b0,z)
ax3.set_xlabel('w')
ax3.set_ylabel('b')
plt.show()

""" b=0
w=np.linspace(0,3,50)
cost = np.zeros_like(w)
for ii_ in range(0,len(w),1):
    cost[ii_]=compute_cost(x_train,y_train,w[ii_],b)

plt.plot(w,cost)
plt.show() """